﻿using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Transactions;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Queues;
using SimpleServiceBus.Bus;
using SimpleServiceBus.Endpoints.Rhino.Transport;

namespace SimpleServiceBus.Tests.Bus.Transport
{
    [TestFixture]
    public class RhinoTransportTestFixture
    {
        private readonly TestRhinoTransport _transport = new TestRhinoTransport();
        
        [TestFixtureSetUp]
        public void SetUp()
        {
        }

        [SetUp]
        public void TestSetUp()
        {
            if (Directory.Exists(_transport.QueueStoragePath))
                Directory.Delete(_transport.QueueStoragePath, true);
        }
        private static MessageEnvelope GetMessageEnvelope(object payload)
        {
            var stream = new MemoryStream();
            new BinaryFormatter().Serialize(stream, payload);

            return new MessageEnvelope {MessageID = Guid.NewGuid().ToString(), Body = payload, Payload = stream.GetBuffer(), ReturnAddress = TestMsmqTransport.Q1};
        }

        [Test]
        public void Custom_Headers_Are_Preserverd_While_Sending_And_Receiving()
        {
            _transport.Start();
            try
            {
                MessageEnvelope message = GetMessageEnvelope("hello_from_rhino");

                message.Headers["intheader"] = "1";
                message.Headers["stringheader"] = "hello";
                message.Headers["dateheader"] = DateTime.Today.ToString();
                message.Headers["timespanheader"] = TimeSpan.FromMinutes(12).ToString();
                message.CorrelationID = "123";

                _transport.Send(message, _transport.ReturnAddress);

                MessageEnvelope received;
                using (var scope = new TransactionScope())
                {
                    received = _transport.GetNextMessage();
                    scope.Complete();
                }

                Assert.IsNotNull(received);
                Assert.AreNotSame(message, received);

                Assert.IsNull(received.Body);
                Assert.That(received.Payload.Length, Is.GreaterThan(0));

                Assert.That(received.Headers.GetValue<int>("intheader"), Is.EqualTo(1));
                Assert.That(received.Headers.GetValue<string>("stringheader"), Is.EqualTo("hello"));
                Assert.That(received.Headers.GetValue<DateTime>("dateheader"), Is.EqualTo(DateTime.Today));
                Assert.That(received.Headers.GetValue<TimeSpan>("timespanheader"), Is.EqualTo(TimeSpan.FromMinutes(12)));
                Assert.That(received.CorrelationID,Is.EqualTo("123"));
            }
            finally
            {
                _transport.Stop();
            }
        }

        [Test]
        public void Get_Next_Message_Gets_Message_From_Queue()
        {
            _transport.Start();
            try
            {
                MessageEnvelope message = GetMessageEnvelope("hello_from_rhino");
                _transport.Send(message, _transport.ReturnAddress);

                MessageEnvelope received;
                using (var scope = new TransactionScope())
                {
                    received = _transport.GetNextMessage();
                    scope.Complete();
                }
                received.Body = new BinaryFormatter().Deserialize(new MemoryStream(received.Payload));

                Assert.That(received.Body, Is.EqualTo(message.Body));
                Assert.That(received.MessageID, Is.EqualTo(message.MessageID));

                
            }
            finally
            {
                _transport.Stop();
            }
        }

        [Test]
        public void Sending_And_Receive_Preserves_Correlation_ID()
        {
            _transport.Start();
            try
            {
                MessageEnvelope message = GetMessageEnvelope("original_message");

                _transport.Send(message, _transport.ReturnAddress);

                using (var scope = new TransactionScope())
                {
                    message = _transport.GetNextMessage();
                    scope.Complete();
                }

                string payload = new BinaryFormatter().Deserialize(new MemoryStream(message.Payload)) as string;
                Assert.That(payload, Is.EqualTo("original_message"));

                MessageEnvelope reply = GetMessageEnvelope("reply_message");
                reply.CorrelationID = message.MessageID;

                _transport.Send(reply, _transport.ReturnAddress);

                using (var scope = new TransactionScope())
                {
                    reply = _transport.GetNextMessage();
                    scope.Complete();
                }
                payload = new BinaryFormatter().Deserialize(new MemoryStream(reply.Payload)) as string;

                Assert.That(payload, Is.EqualTo("reply_message"));
                Assert.That(reply.CorrelationID, Is.EqualTo(message.MessageID));
                Assert.That(reply.CorrelationID, SyntaxHelper.Not.EqualTo(reply.MessageID));
            }
            finally
            {
                _transport.Stop();
            }
        }
      
    }

    public class TestRhinoTransport:RhinoQueuesTransport
    {
        public new MessageEnvelope GetNextMessage() {
            return base.GetNextMessage();}

        protected override void StartService()
        {
            BindToInputQueue();
            //Don't call base, which would start the worker threads
        }
    }
    
}